// 编译模板中vue模板特殊语法，初始化视图、更新视图
// 编译器
// 递归遍历dom树
// 判断节点类型，如果是文本，则判断是否是插值绑定
// 如果是元素，则遍历其属性判断是否是指令或事件，然后递归子元素

class Compiler {
    // el是宿主元素
    // vm是KVue实例
    constructor(el, vm) {
        // 保存kVue实例
        this.$vm = vm
        this.$el = document.querySelector(el)
        if (this.$el) {
            // 执行编译
            this.compile(this.$el)
        }
    }
    compile(el) {
        // 遍历el树
        const childNodes = el.childNodes;
        // console.log(`childNodes`, childNodes)
        Array.from(childNodes).forEach(node => {
            // 判断是否是元素
            if (this.isElement(node)) {
                // console.log('编译元素' + node.nodeName);
                this.compileElement(node)
            } else if (this.isInterpolation(node)) {
                // console.log('编译插值绑定' + node.textContent);
                this.compileText(node)

            }
            // 递归子节点
            if (node.childNodes && node.childNodes.length > 0) {
                this.compile(node)
            }
        })
    }
    isElement(node) {
        return node.nodeType == 1;
    }
    isInterpolation(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
    //编译插值
    compileText(node) {
        console.log(RegExp.$1);
        // node.textContent = this.$vm[RegExp.$1];
        this.update(node, RegExp.$1, 'text')
    }
    //编译元素
    compileElement(node) {
        //节点是元素,遍历其属性列表
        let nodeAttrs = node.attributes;
        // console.log(`nodeAttrs`, nodeAttrs)
        Array.from(nodeAttrs).forEach(attr => {
            // 规定：指令以k-xx="oo"定义 k-text="counter"
            const attrName = attr.name // k-xx k-text
            const exp = attr.value // oo counter
            if (this.isDirective(attrName)) {
                const dir = attrName.substring(2) // xx text
                // 执行指令
                this[dir] && this[dir](node, exp)
            }
        })
    }
    isDirective(attr) {
        return attr.indexOf('k-') === 0
    }
    // k-text
    text(node, exp) {
        // node.textContent = this.$vm[exp];
        this.update(node, exp, 'text')
    }
    //k-html
    html(node, exp) {
        // node.innerHTML = this.$vm[exp]
        this.update(node, exp, 'html')
    }
    // 更新函数作用：
    // 1.初始化
    // 2.创建Watcher实例
    update(node, exp, dir) {
        // 初始化视图，刚开始进入这个页面
        // 指令对应更新函数xxUpdater
        const fn = this[dir + 'Updater']
        fn && fn(node, this.$vm[exp])

        // 更新处理，封装一个更新函数，可以更新对应dom元素
        new Watcher(this.$vm, exp, function (val) {
            fn && fn(node, val)
        })
    }
    textUpdater(node, val) {
        node.textContent = val;
    }
    htmlUpdater(node, val) {
        node.innerHTML = val
    }
}